﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Assist;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using YArchitech.Revit;
using YJKArchUtils.Utils;
using YJKRevitKernel.GeometryUtility;

namespace YJKInspectModel.ElementCollisionChecking
{
	public class CollisionCheckingManager
	{
		public List<ComponentInfo> WallCollisionCheckingResult
		{
			get
			{
				return this.m_WallCollisionCheckingResult;
			}
		}

		public List<ComponentInfo> BeamCollisionCheckingResult
		{
			get
			{
				return this.m_BeamCollisionCheckingResult;
			}
		}

		public List<ComponentInfo> FloorCollisionCheckingResult
		{
			get
			{
				return this.m_FloorCollisionCheckingResult;
			}
		}

		public List<ComponentInfo> ColumnCollisionCheckingResult
		{
			get
			{
				return this.m_ColumnCollisionCheckingResult;
			}
		}

		public CollisionCheckingManager(ExternalCommandData cmdData)
		{
			this.m_Revit = cmdData;
			this.m_Doc = this.m_Revit.Application.ActiveUIDocument.Document;
		}

		[DllImport("user32.dll", EntryPoint = "SendMessageA")]
		public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

		public void RunCollisionChecking()
		{
			this.CollisionChecking();
			new CollisionCheckingResultForm(this).Show(CmdCollisionChecking._hWndRevit);
		}

		public void CollisionChecking()
		{
			List<Level> sortLevels = Common.GetSortLevels(this.m_Doc, double.MinValue, double.MaxValue);
			int key = 0;
			int key2 = 0;
			int key3 = 0;
			int key4 = 0;
			List<List<Element>> walls = this.GetWalls(sortLevels, ref key);
			List<List<Element>> beams = this.GetBeams(sortLevels, ref key2);
			List<List<Element>> floors = this.GetFloors(sortLevels, ref key3);
			List<List<Element>> columns = this.GetColumns(sortLevels, ref key4);
			this.m_Opt = this.m_Revit.Application.Application.Create.NewGeometryOptions();
			this.m_Opt.DetailLevel = this.m_Revit.Application.ActiveUIDocument.ActiveView.DetailLevel;
			this.m_Opt.ComputeReferences = true;
			List<KeyValuePair<int, string>> list = new List<KeyValuePair<int, string>>();
			KeyValuePair<int, string> item = new KeyValuePair<int, string>(key, "墙");
			list.Add(item);
			KeyValuePair<int, string> item2 = new KeyValuePair<int, string>(key2, "梁");
			list.Add(item2);
			KeyValuePair<int, string> item3 = new KeyValuePair<int, string>(key3, "板");
			list.Add(item3);
			KeyValuePair<int, string> item4 = new KeyValuePair<int, string>(key4, "柱");
			list.Add(item4);
			MyProgressBar myProgressBar = new MyProgressBar(list, "碰撞检查", true);
			try
			{
				if (!this.CollisionCheckingTest(walls, ref this.m_WallCollisionCheckingResult, ref myProgressBar))
				{
					myProgressBar.Dispose();
					return;
				}
				if (!this.CollisionCheckingTest(beams, ref this.m_BeamCollisionCheckingResult, ref myProgressBar))
				{
					myProgressBar.Dispose();
					return;
				}
				if (!this.CollisionCheckingTest(floors, ref this.m_FloorCollisionCheckingResult, ref myProgressBar))
				{
					myProgressBar.Dispose();
					return;
				}
				if (!this.CollisionCheckingTest(columns, ref this.m_ColumnCollisionCheckingResult, ref myProgressBar))
				{
					myProgressBar.Dispose();
					return;
				}
			}
			catch (Exception)
			{
				myProgressBar.Dispose();
			}
			myProgressBar.Dispose();
		}

		public void ZoomToElement(int rowIndex, bool zoom)
		{
			List<ComponentInfo> list = new List<ComponentInfo>();
			list.AddRange(this.m_WallCollisionCheckingResult);
			list.AddRange(this.m_BeamCollisionCheckingResult);
			list.AddRange(this.m_FloorCollisionCheckingResult);
			list.AddRange(this.m_ColumnCollisionCheckingResult);
			ElementId componentId = list[rowIndex].ComponentId;
			Element element = this.m_Doc.GetElement(componentId);
			if (element == null)
			{
				return;
			}
			ElementId collisionComponentId = list[rowIndex].CollisionComponentId;
			Element element2 = this.m_Doc.GetElement(collisionComponentId);
			if (element2 == null)
			{
				return;
			}
			UIDocument activeUIDocument = this.m_Revit.Application.ActiveUIDocument;
			if (zoom)
			{
				ElementSet elementSet = new ElementSet();
				elementSet.Insert(element);
				elementSet.Insert(element2);
				activeUIDocument.ShowElements(elementSet);
			}
			List<Element> list2 = new List<Element>();
			list2.Add(element);
			list2.Add(element2);
			RevitVersionFuncs.SetSelection(this.m_Revit.Application.ActiveUIDocument.Selection, list2);
			activeUIDocument.RefreshActiveView();
		}

		public void UnHighlightElements()
		{
			RevitVersionFuncs.ClearSelection(this.m_Revit.Application.ActiveUIDocument.Selection);
		}

		private bool CollisionCheckingTest(List<List<Element>> elementsByFloor, ref List<ComponentInfo> collisionCheckingResult, ref MyProgressBar myProgress)
		{
			List<List<List<Solid>>> list = new List<List<List<Solid>>>();
			foreach (List<Element> list2 in elementsByFloor)
			{
				List<List<Solid>> list3 = new List<List<Solid>>();
				foreach (Element element in list2)
				{
					List<Solid> solids = this.GetSolids(element);
					list3.Add(solids);
				}
				list.Add(list3);
			}
			collisionCheckingResult.Clear();
			for (int i = 0; i < elementsByFloor.Count; i++)
			{
				if (!this.CollisionCheckingTestSameFloor(elementsByFloor[i], list[i], ref collisionCheckingResult, ref myProgress))
				{
					return false;
				}
			}
			return true;
		}

		private bool CollisionCheckingTestSameFloor(List<Element> elements, List<List<Solid>> elementSolidInfoList, ref List<ComponentInfo> collisionCheckingResult, ref MyProgressBar myProgress)
		{
			for (int i = 0; i < elementSolidInfoList.Count; i++)
			{
				Element element = elements[i];
				for (int j = i + 1; j < elementSolidInfoList.Count; j++)
				{
					Element element2 = elements[j];
					this.ElementPairTest(element, elementSolidInfoList[i], element2, elementSolidInfoList[j], ref collisionCheckingResult);
				}
				if (!myProgress.ProgressStep(1) && myProgress.IsStoped)
				{
					return false;
				}
			}
			return true;
		}

		private bool CollisionCheckingTestTowFloor(List<Element> elementsFloor1, List<List<Solid>> elementSolidInfoListFloor1, List<Element> elementsFloor2, List<List<Solid>> elementSolidInfoListFloor2, ref List<ComponentInfo> collisionCheckingResult, ref MyProgressBar myProgress)
		{
			for (int i = 0; i < elementSolidInfoListFloor1.Count; i++)
			{
				Element element = elementsFloor1[i];
				for (int j = i + 1; j < elementSolidInfoListFloor1.Count; j++)
				{
					Element element2 = elementsFloor1[j];
					this.ElementPairTest(element, elementSolidInfoListFloor1[i], element2, elementSolidInfoListFloor1[j], ref collisionCheckingResult);
				}
				for (int j = 0; j < elementSolidInfoListFloor2.Count; j++)
				{
					Element element3 = elementsFloor2[j];
					this.ElementPairTest(element, elementSolidInfoListFloor1[i], element3, elementSolidInfoListFloor2[j], ref collisionCheckingResult);
				}
				if (!myProgress.ProgressStep(1) && myProgress.IsStoped)
				{
					return false;
				}
			}
			return true;
		}

		private void ElementPairTest(Element element1, List<Solid> solidList1, Element element2, List<Solid> solidList2, ref List<ComponentInfo> collisionCheckingResult)
		{
			if (this.CollisionCheckingTest(solidList1, solidList2))
			{
				ElementId id = element1.Id;
				string typeName = this.GetTypeName(element1);
				string levelName = this.GetLevelName(element1);
				ElementId id2 = element2.Id;
				ComponentInfo componentInfo = new ComponentInfo(id, typeName, levelName, id2);
				if (collisionCheckingResult.FindIndex((ComponentInfo value) => value.IsEqual(componentInfo)) == -1)
				{
					collisionCheckingResult.Add(componentInfo);
				}
			}
		}

		private bool CollisionCheckingTest(List<Solid> solidList1, List<Solid> solidList2)
		{
			foreach (Solid solid in solidList1)
			{
				foreach (Solid solid2 in solidList2)
				{
					try
					{
						Solid solid3 = BooleanOperationsUtils.ExecuteBooleanOperation(solid, solid2, (Autodesk.Revit.DB.BooleanOperationsType)2);
						if (null != solid3 && Geometry.GreaterThan(solid3.Volume, 0.0))
						{
							return true;
						}
					}
					catch (Exception ex)
					{
						string message = ex.Message;
					}
				}
			}
			return false;
		}

		private List<Solid> GetSolids(Element element)
		{
			return GeometryUtilityMethod.GetAllSolid(element.get_Geometry(new Options
			{
				ComputeReferences = true
			}), null);
		}

		private List<List<Element>> GetWalls(List<Level> sortedLevels, ref int count)
		{
			List<List<Element>> list = new List<List<Element>>();
			List<KeyValuePair<double, double>> list2 = new List<KeyValuePair<double, double>>();
			for (int i = 0; i < sortedLevels.Count; i++)
			{
				Level level = sortedLevels[i];
				List<Element> item = new List<Element>();
				list.Add(item);
				double elevation = level.Elevation;
				double value;
				if (i == sortedLevels.Count - 1)
				{
					value = elevation + Common.MMToFeet(3000.0);
				}
				else
				{
					value = sortedLevels[i + 1].Elevation;
				}
				list2.Add(new KeyValuePair<double, double>(elevation, value));
			}
			foreach (Element element in new FilteredElementCollector(this.m_Revit.Application.ActiveUIDocument.Document, this.m_Revit.Application.ActiveUIDocument.ActiveView.Id).OfClass(typeof(Wall)).ToElements())
			{
				Wall wall = element as Wall;
				if ((int)wall.WallType.Kind != 1 && (int)wall.WallType.Kind != 2)
				{
					double min = 0.0;
					double max = 0.0;
					this.GetWallElevations(wall, ref min, ref max);
					for (int j = 0; j < list2.Count; j++)
					{
						KeyValuePair<double, double> keyValuePair = list2[j];
						if (this.IsOverlap(min, max, keyValuePair.Key, keyValuePair.Value))
						{
							list[j].Add(wall);
							count++;
						}
					}
				}
			}
			return list;
		}

		private bool IsOverlap(double min1, double max1, double min2, double max2)
		{
			return !Geometry.Greaterthan_Or_Equal(min2, max1) && !Geometry.Lessthan_Or_Equal(max2, min1);
		}

		private void GetWallElevations(Wall wall, ref double bottom, ref double top)
		{
			ElementId elementId = wall.get_Parameter(BuiltInParameter.WALL_BASE_CONSTRAINT).AsElementId();
			Level level = this.m_Doc.GetElement(elementId) as Level;
			bottom = level.Elevation;
			double num = wall.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET).AsDouble();
			bottom += num;
			ElementId elementId2 = wall.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE).AsElementId();
			if (elementId2 == ElementId.InvalidElementId)
			{
				double num2 = wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).AsDouble();
				top = bottom + num2;
				return;
			}
			Level level2 = this.m_Doc.GetElement(elementId2) as Level;
			top = level2.Elevation;
			double num3 = wall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).AsDouble();
			top += num3;
		}

		private List<List<Element>> GetBeams(List<Level> sortedLevels, ref int count)
		{
			List<List<Element>> list = new List<List<Element>>();
			foreach (Level level in sortedLevels)
			{
				List<Element> item = new List<Element>();
				list.Add(item);
			}
			foreach (Element element in new FilteredElementCollector(this.m_Revit.Application.ActiveUIDocument.Document, this.m_Revit.Application.ActiveUIDocument.ActiveView.Id).OfCategory(BuiltInCategory.OST_StructuralFraming).ToElements())
			{
				if (!(element.GetType() != typeof(FamilyInstance)))
				{
					ElementId elementId = element.get_Parameter(BuiltInParameter.INSTANCE_REFERENCE_LEVEL_PARAM).AsElementId();
					Level beamLevel = this.m_Doc.GetElement(elementId) as Level;
					int num = sortedLevels.FindIndex((Level value) => value.Id.IntegerValue == beamLevel.Id.IntegerValue);
					if (num != -1)
					{
						list[num].Add(element);
						count++;
					}
				}
			}
			return list;
		}

		private List<List<Element>> GetColumns(List<Level> sortedLevels, ref int count)
		{
			List<List<Element>> list = new List<List<Element>>();
			List<KeyValuePair<double, double>> list2 = new List<KeyValuePair<double, double>>();
			for (int i = 0; i < sortedLevels.Count; i++)
			{
				Level level = sortedLevels[i];
				List<Element> item = new List<Element>();
				list.Add(item);
				double elevation = level.Elevation;
				double value;
				if (i == sortedLevels.Count - 1)
				{
					value = elevation + Common.MMToFeet(3000.0);
				}
				else
				{
					value = sortedLevels[i + 1].Elevation;
				}
				list2.Add(new KeyValuePair<double, double>(elevation, value));
			}
			foreach (Element element in new FilteredElementCollector(this.m_Revit.Application.ActiveUIDocument.Document, this.m_Revit.Application.ActiveUIDocument.ActiveView.Id).OfClass(typeof(FamilyInstance)).ToElements())
			{
				int integerValue = element.Category.Id.IntegerValue;
				if (integerValue == -2001330 || integerValue == -2000100)
				{
					double min = 0.0;
					double max = 0.0;
					this.GetColumnElevations(element, ref min, ref max);
					for (int j = 0; j < list2.Count; j++)
					{
						KeyValuePair<double, double> keyValuePair = list2[j];
						if (this.IsOverlap(min, max, keyValuePair.Key, keyValuePair.Value))
						{
							list[j].Add(element);
							count++;
						}
					}
				}
			}
			return list;
		}

		private void GetColumnElevations(Element column, ref double bottom, ref double top)
		{
			ElementId elementId = column.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_PARAM).AsElementId();
			Level level = this.m_Doc.GetElement(elementId) as Level;
			bottom = level.Elevation;
			double num = column.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM).AsDouble();
			bottom += num;
			ElementId elementId2 = column.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_PARAM).AsElementId();
			Level level2 = this.m_Doc.GetElement(elementId2) as Level;
			top = level2.Elevation;
			double num2 = column.get_Parameter(BuiltInParameter.SCHEDULE_TOP_LEVEL_OFFSET_PARAM).AsDouble();
			top += num2;
		}

		private List<List<Element>> GetFloors(List<Level> sortedLevels, ref int count)
		{
			List<List<Element>> list = new List<List<Element>>();
			foreach (Level level in sortedLevels)
			{
				List<Element> item = new List<Element>();
				list.Add(item);
			}
			foreach (Element element in new FilteredElementCollector(this.m_Revit.Application.ActiveUIDocument.Document, this.m_Revit.Application.ActiveUIDocument.ActiveView.Id).OfClass(typeof(Floor)).ToElements())
			{
				Floor floor = element as Floor;
				Level floorLevel = floor.GetYJKLevel();
				int num = sortedLevels.FindIndex((Level value) => value.Id.IntegerValue == floorLevel.Id.IntegerValue);
				if (num != -1)
				{
					list[num].Add(floor);
					count++;
				}
			}
			return list;
		}

		private string GetTypeName(Element element)
		{
			string result = "--";
			ElementId typeId = element.GetTypeId();
			Element element2 = this.m_Doc.GetElement(typeId);
			if (element2 == null)
			{
				return result;
			}
			return element2.Name;
		}

		private string GetLevelName(Element element)
		{
			ElementId elementId = ElementId.InvalidElementId;
			if (element.GetType() == typeof(FamilyInstance))
			{
				if (element.Category.Id.IntegerValue == -2000100 || element.Category.Id.IntegerValue == -2001330)
				{
					elementId = element.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_PARAM).AsElementId();
				}
				else
				{
					elementId = element.get_Parameter(BuiltInParameter.INSTANCE_REFERENCE_LEVEL_PARAM).AsElementId();
				}
			}
			else if (element.GetType() == typeof(Wall))
			{
				elementId = element.get_Parameter(BuiltInParameter.WALL_BASE_CONSTRAINT).AsElementId();
			}
			else if (element.GetType() == typeof(Floor))
			{
				elementId = element.get_Parameter(BuiltInParameter.LEVEL_PARAM).AsElementId();
			}
			string result = "--";
			if (elementId == null || elementId == ElementId.InvalidElementId)
			{
				return result;
			}
			Element element2 = this.m_Doc.GetElement(elementId);
			if (element2 == null)
			{
				return result;
			}
			return element2.Name;
		}

		private ExternalCommandData m_Revit;

		private Document m_Doc;

		private Options m_Opt;

		private List<ComponentInfo> m_WallCollisionCheckingResult = new List<ComponentInfo>();

		private List<ComponentInfo> m_BeamCollisionCheckingResult = new List<ComponentInfo>();

		private List<ComponentInfo> m_FloorCollisionCheckingResult = new List<ComponentInfo>();

		private List<ComponentInfo> m_ColumnCollisionCheckingResult = new List<ComponentInfo>();
	}
}
